package cn.com.generaldata.os.service.impl;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.StopWatch;
import org.nutz.castor.Castors;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Condition;
import org.nutz.dao.Dao;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.entity.Record;
import org.nutz.dao.impl.sql.SqlTemplate;
import org.nutz.dao.sql.Criteria;
import org.nutz.dao.util.cri.Exps;
import org.nutz.el.El;
import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.random.R;
import org.nutz.lang.util.Context;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.com.generaldata.jsme_indicator_system.entity.IndicatorAccRefGroupSet;
import cn.com.generaldata.jsme_indicator_system.entity.IndicatorAccounting;
import cn.com.generaldata.jsme_indicator_system.entity.IndicatorAccountingReference;
import cn.com.generaldata.jsme_indicator_system.entity.IndicatorInput;
import cn.com.generaldata.os.newpojo.ReportType;
import cn.com.generaldata.jsme_indicator_system.entity.ReportTypeIndicator;
import cn.com.generaldata.jsme_indicator_system.util.Constants;
import cn.com.generaldata.jsme_indicator_system.util.Constants.IndicatorAccountingReferenceType;
import cn.com.generaldata.os.util.BeanMapper;
import cn.com.generaldata.os.vo.IndicatorAccountVO;
import cn.com.generaldata.os.vo.ReportVO;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;

/**
 * 计算核算指标service.
 * 通过核算指标的定义公式，取值运算，并将结果保存在数据库中.
 */
@Service
public class CalcuIndicatorAccoutService {

	private static final Logger logger = LoggerFactory.getLogger(CalcuIndicatorAccoutService.class);

	/** 指标取值方式 - 取值 */
	private static final int GET_VALUE_TYPE_GET = 1;
	/** 指标取值方式 - 汇总 **/
	private static final int GET_VALUE_TYPE_SUM = 2;

	private static final String provinceAreaId = "320000000000000";

	/** 核算市级地区 */
	private static final String[] cityAreaIds = { "320600000000000", "320700000000000", "320900000000000" };

	@Autowired
	private Dao indicatorSystemDao;

	@Autowired
	private Dao dao;

	@Autowired
	private SqlTemplate sqlTemplate;

	@Autowired
	private SqlTemplate indicatorSystemSqlTemplate;

	@Autowired
	private UnitService unitService;

	// private ExecutorService executorService = Executors.newSingleThreadExecutor();

	private final Pattern pattern = Pattern.compile("e_\\d+_\\d+");

	// private String indicatorName = "海水利用业增加值%";

	private List indicatorProcess = null;
	
	/**
	 * 计算核算公式生成核算指标结果.
	 * 
	 * @param indicatorAccountVO
	 */
	public void calculate(IndicatorAccountVO indicatorAccountVO) {

		// 生成省级核算指标
		calculateProvinceIndicatorAccount(indicatorAccountVO);
		// 生成市级核算指标
		calculateCityIndicatorAccount(indicatorAccountVO);

	}

	/**
	 * 生成省级核算指标
	 * 
	 * @param indicatorAccountVO
	 */
	private void calculateProvinceIndicatorAccount(IndicatorAccountVO indicatorAccountVO) {
		// Condition cnd = Cnd.where(Exps.inInt("section", 1, 5)).and("name", "like", indicatorName).desc("priority");
		Condition cnd = Cnd.where(Exps.inInt("section", 1, 5)).desc("priority");
		List<IndicatorAccounting> indicatorAccountings = indicatorSystemDao.query(IndicatorAccounting.class, cnd);
		IndicatorAccountVO provinceIndicatorAccountVO = BeanMapper.map(indicatorAccountVO, IndicatorAccountVO.class);
		provinceIndicatorAccountVO.setAreaId(provinceAreaId);
		calculateInner(provinceIndicatorAccountVO, indicatorAccountings);
	}

	/**
	 * 生成市级核算指标.
	 * 
	 * @param indicatorAccountVO
	 */
	private void calculateCityIndicatorAccount(IndicatorAccountVO indicatorAccountVO) {
		// Condition cnd = Cnd.where(Exps.inInt("section", 2, 5, 6)).and("name", "like",
		// indicatorName).desc("priority");
		Condition cnd = Cnd.where(Exps.inInt("section", 2, 5, 6)).desc("priority");
		List<IndicatorAccounting> indicatorAccountings = indicatorSystemDao.query(IndicatorAccounting.class, cnd);
		// 生新生成核算指标
		for (String areaId : cityAreaIds) {
			IndicatorAccountVO cityIndicatorAccountVO = BeanMapper.map(indicatorAccountVO, IndicatorAccountVO.class);
			cityIndicatorAccountVO.setAreaId(areaId);

			calculateInner(cityIndicatorAccountVO, indicatorAccountings);
			// calculateCountyIndicatorAccount(areaId, cityIndicatorAccountVO);
		}
	}

	/**
	 * 生成县级核算指标.
	 * 
	 * @param cityAreaId
	 * @param indicatorAccountVO
	 */
	private void calculateCountyIndicatorAccount(String cityAreaId, IndicatorAccountVO indicatorAccountVO) {
		List<Record> records = dao.query("tbl_area", Cnd.where("parent_id", "=", cityAreaId).asc("id"));
		Condition cnd = Cnd.where(Exps.inInt("section", 4, 5, 6)).desc("priority");
		List<IndicatorAccounting> indicatorAccountings = indicatorSystemDao.query(IndicatorAccounting.class, cnd);
		for (Record record : records) {
			IndicatorAccountVO countyIndicatorAccountVO = BeanMapper.map(indicatorAccountVO, IndicatorAccountVO.class);
			countyIndicatorAccountVO.setAreaId(record.getString("id"));
			calculateInner(countyIndicatorAccountVO, indicatorAccountings);
		}
	}

	/**
	 * 对单位核算指标进行计算.
	 * 
	 * @param accountingResultId 核算指标结果id.
	 */
	public void calculateSingleIndicator(String accountingResultId) {
		Record accountingResult = dao.fetch("TBL_INDICATOR_ACCOUNT", Cnd.where("ID", "=", accountingResultId));

		int indicatorAccountId = accountingResult.getInt("INDICATOR_ID");
		IndicatorAccounting indicatorAccounting = indicatorSystemDao.fetch(IndicatorAccounting.class,
				indicatorAccountId);

		IndicatorAccountVO vo = getAccountVOFromResult(accountingResult);
		// 计算本指标
		Double result = calculateIndicator(vo, indicatorAccounting);
		saveIndicator(vo, indicatorAccounting, result);
		// 计算关联指标
		calculateRefedIndicator(indicatorAccounting, vo);
	}

	/**
	 * 根据核算指标结果构造核算指标计算条件.
	 * 
	 * @param accountingResult 核算指标结果
	 * @return 核算指标计算条件
	 */
	private IndicatorAccountVO getAccountVOFromResult(Record accountingResult) {
		String areaId = accountingResult.getString("AREA_ID");
		int year = accountingResult.getInt("YEAR");
		int timeType = accountingResult.getInt("TIME_TYPE");
		int yearTime = accountingResult.getInt("YEAR_TIME");
		int quarter = accountingResult.getInt("QUARTER");
		int month = accountingResult.getInt("MONTH");

		IndicatorAccountVO vo = new IndicatorAccountVO();
		vo.setAreaId(areaId);
		vo.setTimeType(timeType);
		vo.setYear(year);
		if (yearTime != -1) {
			vo.setYearTime(yearTime);
		}
		if (quarter != -1) {
			vo.setQuarter(quarter);
		}

		if (month != -1) {
			vo.setMonth(month);
		}

		return vo;
	}

	/**
	 * 对引用该核算指标的核算指标进行计算
	 * 
	 * @param indicatorAccounting 进行单指标核算的核算指标信息
	 * @param vo 核算计算条件
	 */
	private void calculateRefedIndicator(IndicatorAccounting indicatorAccounting, IndicatorAccountVO vo) {
		List<IndicatorAccountingReference> refs = getIndicatorAccountingRefs(indicatorAccounting.getId(),
				vo.getAreaId());
		for (IndicatorAccountingReference iar : refs) {
			IndicatorAccounting refedIndicatorAccounting = dao.fetch(IndicatorAccounting.class,
					iar.getIndicatorAccountingId());

			// 计算该关联指标
			Double result = calculateIndicator(vo, refedIndicatorAccounting);
			saveIndicator(vo, refedIndicatorAccounting, result);
			calculateRefedIndicator(refedIndicatorAccounting, vo);
		}
	}

	/**
	 * 获得引用该核算指标id进行核算
	 * 
	 * @param indicatorAccountId
	 * @return
	 */
	public List<IndicatorAccountingReference> getIndicatorAccountingRefs(Integer indicatorAccountId, String areaId) {
		String sql = "select iar.* from t_indicator_accounting_ref  iar,t_indicator_accounting  ia where iar.reference_indicator_id="
				+ " @indicatorAccountId AND ia.section IN (@sections) "
				+ " and iar.reference_indicator_type=5 and iar.indicator_accounting_id=ia.id ORDER BY ia.priority desc";
		Map<String, Object> params = Maps.newHashMap();
		params.put("indicatorAccountId", indicatorAccountId);
		List<Integer> sections = getIndicatorSectionsFromAreaId(areaId);
		params.put("sections", sections);
		Entity<IndicatorAccountingReference> entity = indicatorSystemDao.getEntity(IndicatorAccountingReference.class);
		List<IndicatorAccountingReference> indicatorAccountingRefs = sqlTemplate.query(sql, params, entity);
		return indicatorAccountingRefs;
	}

	/**
	 * 根据地区id获得核算指标对应的地区级别
	 * 
	 * @param areaId 地区id
	 * @return 核算指标对应的地区级别
	 */
	private List<Integer> getIndicatorSectionsFromAreaId(String areaId) {
		List<Integer> result = null;
		boolean isProvince = provinceAreaId.equals(areaId);
		if (isProvince) {
			result = ImmutableList.of(1, 5);
		}
		boolean isCity = ArrayUtils.contains(cityAreaIds, areaId);
		if (isCity) {
			result = ImmutableList.of(2, 5, 6);
		}

		if (!isProvince && !isCity) {
			result = ImmutableList.of(4, 5, 6);
		}
		return result;
	}

	/**
	 * 计算核算公式生成结果.
	 * 
	 * @param indicatorAccountVO 核算条件
	 * @param areaId 地区id
	 */
	private void calculateInner(IndicatorAccountVO indicatorAccountVO, List<IndicatorAccounting> indicatorAccountings) {

		logger.info("开始进行核算公式的运算，核算信息：{},核算地区：{}", indicatorAccountVO, indicatorAccountVO.getAreaId());
		StopWatch watch = new StopWatch();
		watch.start();

		for (IndicatorAccounting indicatorAccounting : indicatorAccountings) {

			try {
				Double indicatorResult = calculateIndicator(indicatorAccountVO, indicatorAccounting);

				logger.debug("核算公式：{}，核算公式结果:{}", indicatorAccounting, indicatorResult);

				// 指标不为空，保存到数据库中.
				if (indicatorResult != null) {
					saveIndicator(indicatorAccountVO, indicatorAccounting, indicatorResult);
				}

			} catch (Exception e) {
				logger.error(String.format("核算公式运算异常,核算指标信息 :%s", indicatorAccounting), e);
				saveIndicator(indicatorAccountVO, indicatorAccounting, Double.valueOf("0"));
			}

		}

		watch.stop();
		logger.info("核算公式运算结束，所花时间：{} 秒", watch.getTime() / 1000);
	}

	public Double calculateIndicator(IndicatorAccountVO indicatorAccountVO, IndicatorAccounting indicatorAccounting) {
		indicatorSystemDao.fetchLinks(indicatorAccounting, null);
		Double indicatorResult = null;
		if (indicatorAccounting.getReferences().isEmpty()) {
			throw Lang.makeThrow("核算公式的不存在相关的引用指标，无法进行运算,核算指标信息:%s", indicatorAccounting);
		}

		
		
		if (needElCalculate(indicatorAccounting.getFormula())) {

			Context ctx = Lang.context();
			for (IndicatorAccountingReference reference : indicatorAccounting.getReferences()) {
				Double referenceIndicatorVal = getIndicatorAccountingReferenceValue(indicatorAccounting, reference,
						indicatorAccountVO);
				ctx.set(reference.getFormulaName(), referenceIndicatorVal);
			}
			try {
				Object evalResult = El.eval(ctx, indicatorAccounting.getFormula());
				indicatorResult = Castors.me().castTo(evalResult, Double.class);
			} catch (Exception e) {
				logger.error("运算核算公式发生错误，核算指标信息 : {}", Json.toJson(indicatorAccounting));
				logger.error("", e);
				indicatorResult = Double.valueOf("0");
			}

		} else {

			IndicatorAccountingReference reference = indicatorAccounting.getReferences().get(0);

			indicatorResult = getIndicatorAccountingReferenceValue(indicatorAccounting, reference, indicatorAccountVO);

		}

		if (Double.isNaN(indicatorResult) || Double.isInfinite(indicatorResult)) {
			indicatorResult = Double.valueOf("0");
		}
		
		Double indicatorValue = indicatorResult / indicatorAccounting.getUnitMagnitude();
		//核算过程最终结果
		setIndicatorProcess("<B>核算指标汇总</B>", indicatorAccounting.getUnit(), indicatorAccounting.getName(), indicatorValue.toString(), indicatorAccounting.getShowFormula(), indicatorAccounting.getId().toString());
		
		return indicatorResult;

	}

	/***
	 * 检查核算公式是否需要使用El进行运算
	 * 
	 * @param formula
	 * @return
	 */
	private boolean needElCalculate(String formula) {

		Matcher matcher = pattern.matcher(formula);
		int count = 0;
		while (matcher.find()) {
			count++;
		}
		return count > 1;
	}

	/**
	 * 保存核算指标到数据库
	 * 
	 * @param indicatorAccountVO
	 * @param areaId
	 * @param indicatorAccounting
	 * @param indicatorResult
	 */
	private void saveIndicator(IndicatorAccountVO indicatorAccountVO, IndicatorAccounting indicatorAccounting,
			Double indicatorResult) {

		// 先把老的指标查询出来
		Criteria cri = getIndicatorAccountingDataCri(indicatorAccountVO, indicatorAccounting,
				indicatorAccountVO.getAreaId(), indicatorAccountVO.getYear());
		// 取该指标修改过的指标值
		Record record = dao.fetch("tbl_indicator_account", cri);
		Double editValue = null;
		Double editValueGrowthRate = null;
		if (record != null) {
			if (record.get("edit_value") != null) {
				editValue = Castors.me().castTo(record.get("edit_value"), Double.class);
			}
			if (record.get("edit_value_growth_rate") != null) {
				editValueGrowthRate = Castors.me().castTo(record.get("edit_value_growth_rate"), Double.class);
			}
		}

		// 删除老的记录
		dao.clear("tbl_indicator_account", cri);

		// 保存新记录
		Chain chain = Chain.make("id", R.UU16());
		chain.add("indicator_id", indicatorAccounting.getId());
		chain.add("area_id", indicatorAccountVO.getAreaId());
		chain.add("time_type", indicatorAccountVO.getTimeType());
		chain.add("year", indicatorAccountVO.getYear());
		if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_MONTH) {
			chain.add("month", indicatorAccountVO.getMonth());
		} else if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
			chain.add("year_time", indicatorAccountVO.getYearTime());
		} else {
			chain.add("quarter", indicatorAccountVO.getQuarter());
		}
		Double reducingIndicatorResult = indicatorResult / indicatorAccounting.getUnitMagnitude();
		// BigDecimal iv = new BigDecimal(reducingIndicatorResult);
		// iv.setScale(15, BigDecimal.ROUND_HALF_UP);
		chain.add("indicator_value", String.format("%.2f", reducingIndicatorResult));
		editValue = (editValue == null || Double.valueOf("0").equals(editValue)) ? reducingIndicatorResult : editValue;
		chain.add("edit_value", String.format("%.2f", editValue));

		calcuIndicatorGrowthRate(indicatorAccountVO, indicatorAccounting, editValueGrowthRate, chain,
				reducingIndicatorResult, editValue);
		calcuIndicatorYearBookGrowthRate(indicatorAccountVO, indicatorAccounting, chain, reducingIndicatorResult,
				editValue);
		IndicatorAccountVO lastIndicatorAccount = BeanMapper.map(indicatorAccountVO, IndicatorAccountVO.class);
		lastIndicatorAccount.setYear(indicatorAccountVO.getYear() - 1);
		calcuIndicatorLastYearBookGrowthRate(lastIndicatorAccount, indicatorAccounting, chain, reducingIndicatorResult,
				editValue);
		dao.insert("tbl_indicator_account", chain);
	}

	/**
	 * 计算核算指标核算结果与修改结果去年年鉴的增加速度.
	 */
	private void calcuIndicatorLastYearBookGrowthRate(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccounting indicatorAccounting, Chain chain, Double reducingIndicatorResult, Double editValue) {
		if (indicatorAccounting.getEqualIndicatorInputId() == null) {
			return;
		}
		Double yearBookIndicatorValue = getYearBookIndicatorValue(indicatorAccountVO, indicatorAccounting);
		if (yearBookIndicatorValue == null || yearBookIndicatorValue.isNaN() || yearBookIndicatorValue.isInfinite()) {
			return;
		}
		Double ivgr = (reducingIndicatorResult - yearBookIndicatorValue) / yearBookIndicatorValue * 100;
		if (ivgr.isNaN() || ivgr.isInfinite()) {
			ivgr = Double.valueOf("0");
		}

		chain.add("iv_last_year_book_growth_rate", String.format("%.2f", ivgr));

		Double evgr = (editValue - yearBookIndicatorValue) / yearBookIndicatorValue * 100;
		if (evgr.isNaN() || evgr.isInfinite()) {
			evgr = Double.valueOf("0");
		}

		chain.add("ev_last_year_book_growth_rate", String.format("%.2f", evgr));
	}

	/**
	 * 计算核算指标核算结果与修改结果对应年鉴的增加速度.
	 */
	private void calcuIndicatorYearBookGrowthRate(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccounting indicatorAccounting, Chain chain, Double reducingIndicatorResult, Double editValue) {
		if (indicatorAccounting.getEqualIndicatorInputId() == null) {
			return;
		}
		Double yearBookIndicatorValue = getYearBookIndicatorValue(indicatorAccountVO, indicatorAccounting);
		if (yearBookIndicatorValue == null || yearBookIndicatorValue.isNaN() || yearBookIndicatorValue.isInfinite()) {
			return;
		}
		Double ivgr = (reducingIndicatorResult - yearBookIndicatorValue) / yearBookIndicatorValue * 100;
		if (ivgr.isNaN() || ivgr.isInfinite()) {
			ivgr = Double.valueOf("0");
		}

		chain.add("iv_year_book_growth_rate", String.format("%.2f", ivgr));

		Double evgr = (editValue - yearBookIndicatorValue) / yearBookIndicatorValue * 100;
		if (evgr.isNaN() || evgr.isInfinite()) {
			evgr = Double.valueOf("0");
		}

		chain.add("ev_year_book_growth_rate", String.format("%.2f", evgr));
	}

	/**
	 * 获取核算对应年鉴指标值.
	 * 
	 * @param indicatorAccountVO
	 * @param indicatorAccounting
	 * @return
	 */
	public Double getYearBookIndicatorValue(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccounting indicatorAccounting) {
		Criteria cri = Cnd.cri();
		cri.where().and("indicator_id", "=", indicatorAccounting.getEqualIndicatorInputId());
		cri.where().and("time_type", "=", indicatorAccountVO.getTimeType());
		cri.where().and("year", "=", indicatorAccountVO.getYear());
		cri.where().and("area_id", "=", indicatorAccountVO.getAreaId());
		if (indicatorAccountVO.getTimeType().intValue() == Constants.REPORT_TIME_MONTH) {
			cri.where().and("month", "=", indicatorAccountVO.getMonth());
		} else if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
			cri.where().and("year_time", "=", indicatorAccountVO.getYearTime());
		} else {
			cri.where().and("quarter", "=", indicatorAccountVO.getQuarter());
		}

		List<Record> records = dao.query("tbl_indicator_data", cri);
		if (records.isEmpty()) {
			return null;
		}

		// Double indicatotUnitMagnitude = 1d;
		// IndicatorInput indicatorInput = indicatorSystemDao.fetch(IndicatorInput.class,
		// indicatorAccounting.getEqualIndicatorInputId());
		// if (indicatorInput != null) {
		// indicatotUnitMagnitude = indicatorInput.getUnitMagnitude();
		// }
		//
		Record record = records.get(0);
		Double result = Castors.me().castTo(record.get("indicator_value"), Double.class);

		return result;

	}

	/**
	 * 计算核算值的增长速度.
	 * 
	 * @param indicatorAccountVO 核算条件
	 * @param indicatorAccounting 核算指标
	 * @param editValueGrowthRate 修正值的增长速度
	 * @param chain 保存的指标对象
	 * @param reducingIndicatorResult 核算指标值
	 */
	private void calcuIndicatorGrowthRate(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccounting indicatorAccounting, Double editValueGrowthRate, Chain chain,
			Double reducingIndicatorResult, Double editValue) {
		// 获取上一期的数据计算增长速度
		// 查询上期指标
		Criteria prevCri = getIndicatorAccountingDataCri(indicatorAccountVO, indicatorAccounting,
				indicatorAccountVO.getAreaId(), indicatorAccountVO.getYear() - 1);
		Record prevRecord = dao.fetch("tbl_indicator_account", prevCri);
		if (prevRecord != null) {
			Double prevIndicatorValue = Castors.me().castTo(prevRecord.get("indicator_value"), Double.class);
			if (prevIndicatorValue != null) {
				Double ivgr = (reducingIndicatorResult - prevIndicatorValue) / prevIndicatorValue * 100;
				if (ivgr.isNaN() || ivgr.isInfinite()) {
					ivgr = Double.valueOf("0");
				}
				chain.add("indicator_value_growth_rate", String.format("%.2f", ivgr));

			}
			Double prevEditValue = Castors.me().castTo(prevRecord.get("edit_value"), Double.class);
			if (prevEditValue != null) {
				Double evgr = (editValue - prevEditValue) / prevEditValue * 100;
				if (evgr.isNaN() || evgr.isInfinite()) {
					evgr = Double.valueOf("0");
				}
				chain.add("edit_value_growth_rate", String.format("%.2f", evgr));
			}
		} else {
			chain.add("indicator_value_growth_rate", 0);
			chain.add("edit_value_growth_rate", 0);
		}
	}

	public Criteria getIndicatorAccountingDataCri(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccounting indicatorAccounting, String areadId, int year) {
		Criteria cri = Cnd.cri();
		cri.where().and("indicator_id", "=", indicatorAccounting.getId());
		cri.where().and("time_type", "=", indicatorAccountVO.getTimeType());
		cri.where().and("year", "=", year);
		cri.where().and("area_id", "=", areadId);
		if (indicatorAccountVO.getTimeType().intValue() == Constants.REPORT_TIME_MONTH) {
			cri.where().and("month", "=", indicatorAccountVO.getMonth());
		} else if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
			cri.where().and("year_time", "=", indicatorAccountVO.getYearTime());
		} else {
			cri.where().and("quarter", "=", indicatorAccountVO.getQuarter());
		}
		return cri;
	}

	/**
	 * 获取核算指标的引用指标指标值.
	 * 
	 * @param reference 核算公式引用的指标
	 * @param indicatorAccountVO 核算公式运行条件
	 */
	private Double getIndicatorAccountingReferenceValue(IndicatorAccounting indicatorAccounting,
			IndicatorAccountingReference reference, IndicatorAccountVO indicatorAccountVO) {
		Double result = null;
		if (IndicatorAccountingReferenceType.INPUT.getValue() == reference.getReferenceIndicatorType()) {
			result = getValueFromIndicatorInput(reference, indicatorAccountVO);			
		} else if (IndicatorAccountingReferenceType.ACCOUNTING.getValue() == reference.getReferenceIndicatorType()) {
			result = getValueFromIndicatorAccounting(indicatorAccounting, reference, indicatorAccountVO);
		} else if (IndicatorAccountingReferenceType.INDICATOR_GROUP.getValue() == reference.getReferenceIndicatorType()) {
			Integer indicatorId = getIndicatorIdFromGroup(indicatorAccountVO, reference);
			result = getValueFromReport(indicatorAccounting, reference, indicatorId, indicatorAccountVO);
		} else {
			result = getValueFromReport(indicatorAccounting, reference, reference.getReferenceIndicatorId(),
					indicatorAccountVO);
		}
		return result;
	}

	/**
	 * 根据指标引用组和地区获取引用的报表指标id
	 * 
	 * @param indicatorAccountVO
	 * @param reference
	 * @return
	 */
	private Integer getIndicatorIdFromGroup(IndicatorAccountVO indicatorAccountVO,
			IndicatorAccountingReference reference) {
		Cnd cnd = Cnd.where("groupId", "=", reference.getReferenceIndicatorId()).and("areaId", "=",
				indicatorAccountVO.getAreaId());
		IndicatorAccRefGroupSet iags = indicatorSystemDao.fetch(IndicatorAccRefGroupSet.class, cnd);
		Validate.notNull(iags, "无法获取核算引用组引用的指标,核算指标组id ：%d ,地区id ：%s", reference.getReferenceIndicator(),
				indicatorAccountVO.getAreaId());
		return iags.getIndicatorId();
	}

	/**
	 * 从核算指标中取值
	 * 
	 * @param indicatorAccounting 当前进行计算的核算指标
	 * @param reference 当前进行计算的核算指标的引用指标
	 * @param indicatorAccountVO 核算条件
	 * @return
	 */
	private Double getValueFromIndicatorAccounting(IndicatorAccounting indicatorAccounting,
			IndicatorAccountingReference reference, IndicatorAccountVO indicatorAccountVO) {
		Double result;
		IndicatorAccounting referenceIndicatorAccounting = indicatorSystemDao.fetch(IndicatorAccounting.class,
				reference.getReferenceIndicatorId());
		String areaId = indicatorAccountVO.getAreaId();
		// 判断引用的核算指标是否与当前进行运算的核算指标级别一致
		if (!indicatorAccounting.getSection().equals(referenceIndicatorAccounting.getSection())) {
			// 省级
			if (referenceIndicatorAccounting.getSection() == 1) {
				areaId = provinceAreaId;
			} else if (referenceIndicatorAccounting.getSection() == 2) {
				// 市级
				areaId = StringUtils.left(areaId, 4) + "00000000000";
			}
		}

		Criteria cri = getIndicatorAccountingDataCri(indicatorAccountVO, referenceIndicatorAccounting, areaId,
				indicatorAccountVO.getYear() + reference.getPhase());
		//
		Record record = dao.fetch("tbl_indicator_account", cri);
		if (record == null) {
			IndicatorAccountVO referenceIndicatorAccountVO = BeanMapper.map(indicatorAccountVO,
					IndicatorAccountVO.class);
			referenceIndicatorAccountVO.setYear(indicatorAccountVO.getYear() + reference.getPhase());

			result = calculateIndicator(referenceIndicatorAccountVO, referenceIndicatorAccounting);
		} else {
			result = Castors.me().castTo(record.get("indicator_value"), Double.class);
		}
		
		result = reducingResultMagnitude(result, reference.getReferenceIndicatorType(),
				reference.getReferenceIndicatorId());
		
		//记录核算指标信息
		setIndicatorProcess("核算指标", reducingResultUnit(reference.getReferenceIndicatorType(),
				reference.getReferenceIndicatorId()), referenceIndicatorAccounting.getName(), result.toString(), referenceIndicatorAccounting.getName(), referenceIndicatorAccounting.getId().toString());
		
		return result;
	}

	/***
	 * 获取录入指标指标值
	 * 
	 * @param reference
	 * @param indicatorAccountVO
	 */
	private Double getValueFromIndicatorInput(IndicatorAccountingReference reference,
			IndicatorAccountVO indicatorAccountVO) {
		IndicatorInput indicator = indicatorSystemDao.fetch(IndicatorInput.class, reference.getReferenceIndicatorId());
		String sql = "SELECT indicator_value, year FROM tbl_indicator_data WHERE indicator_id = @indicatorId ";
		Map<String, Object> params = Maps.newHashMap();
		params.put("indicatorId", indicator.getId());
		params.put("timetype", indicatorAccountVO.getTimeType());
		params.put("year", indicatorAccountVO.getYear() + reference.getPhase());
		// 是否可获取上一期数据
		if (indicator.getCanGetLastTimeData() == 1) {
			if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_MONTH) {
				sql += " AND time_type = @timetype AND year <= @year AND month = @month ";
				params.put("month", indicatorAccountVO.getMonth());
			} else if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
				sql += " AND time_type = @timetype AND year <= @year AND year_time = @yearTime ";
				params.put("yearTime", indicatorAccountVO.getYearTime());
			} else {
				sql += " AND time_type = @timetype AND year <= @year AND quarter = @quarter ";
				params.put("quarter", indicatorAccountVO.getQuarter());
			}
		} else {
			if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_MONTH) {
				sql += " AND time_type = @timetype AND year = @year AND month = @month ";
				params.put("month", indicatorAccountVO.getMonth());
			} else if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
				sql += " AND time_type = @timetype AND year = @year AND year_time = @yearTime ";
				params.put("yearTime", indicatorAccountVO.getYearTime());
			} else {
				sql += " AND time_type = @timetype AND year = @year AND quarter = @quarter ";
				params.put("quarter", indicatorAccountVO.getQuarter());
			}
		}
		// 各级数据是否一致
		if (indicator.getDataIsUnique() == 0) {
			sql += " AND area_id = @areaId ";
			params.put("areaId", indicatorAccountVO.getAreaId());
		}

		sql += " ORDER BY year DESC ";

		Record record = sqlTemplate.queryForRecord(sql, params);
		String year = record.getString("year");
		
		Double indicatorValue = Double.parseDouble(record.getString("indicator_value"));
		
		// 指标值不存在，抛出异常，提示公式无法进行运算
		if (indicatorValue == null) {
			throw Lang.makeThrow("指标值不存在，指标信息：%s ", Json.toJson(indicator));
		}
		Double result = reducingResultMagnitude(indicatorValue, reference.getReferenceIndicatorType(),
				reference.getReferenceIndicatorId());
		
		//记录录入指标信息
		setIndicatorProcess("录入指标", reducingResultUnit(reference.getReferenceIndicatorType(),
				reference.getReferenceIndicatorId()), indicator.getName(), result.toString(), "（" + year + "）" + indicator.getName(), indicator.getId().toString());
		
		return result;
	}

	/**
	 * 从上报的报表中获得指标值
	 * 
	 * @return
	 */
	private Double getValueFromReport(IndicatorAccounting indicatorAccounting, IndicatorAccountingReference reference,
			Integer referenceIndicatorId, IndicatorAccountVO indicatorAccountVO) {

		Map<String, Object> params = Maps.newHashMap();
		Map<String, Object> vars = Maps.newHashMap();

		addIndicatorParam(referenceIndicatorId, indicatorAccountVO, vars, params);

		ReportTimeVO reportTimeVO = frequencyConverter(indicatorAccounting, referenceIndicatorId, indicatorAccountVO);
		params.put("provinceUnitId", unitService.getProvinceOceanUnit().getId());
		params.put("reportTimetype", reportTimeVO.timeType);
		params.put("reportYear", reportTimeVO.year);
		String sql = "SELECT ";
		if (reference.getGetValueType() == GET_VALUE_TYPE_GET) {
			sql += " rd.$clomun ";
		} else {
			sql += " SUM(rd.$clomun) ";
		}
		sql += "FROM tbl_report r INNER JOIN tbl_report_detail rd ON r.id = rd.report_id "
				+ " INNER JOIN tbl_user u ON r.report_user_id = u.id " + " WHERE r.report_type_id = @reportTypeId  ";

		if (params.containsKey("row")) {
			sql += " AND rd.row_index = @row ";
		}
		// 取值时，直接取对应的地区id报表数据
		sql += " AND u.user_area_id = @areaId ";
		params.put("areaId", indicatorAccountVO.getAreaId());

		// if (reference.getGetValueType() == GET_VALUE_TYPE_GET) {
		// sql += " AND u.user_area_id = @areaId ";
		// params.put("areaId", indicatorAccountVO.getAreaId());
		// } else {
		// // 汇总时，要取在范围内容的所有企业
		// sql += " AND u.user_area_id LIKE  @areaPrefix ";
		// String areaId = indicatorAccountVO.getAreaId();
		// String areaPrefix = "";
		// if (areaId.substring(2, 4).equals("00")) {
		// areaPrefix = StringUtils.left(areaId, 2) + "%";
		// } else {
		// areaPrefix = StringUtils.left(areaId, 4) + "%";
		// }
		// params.put("areaPrefix", areaPrefix);
		// }

		// 汇总时间频率条件SQL
		if (reportTimeVO.timeType == Constants.REPORT_TIME_MONTH) {
			sql += " AND r.report_timetype = @reportTimetype AND r.report_year = @reportYear AND r.month = @month ";
			params.put("month", reportTimeVO.month);
		} else if (reportTimeVO.timeType == Constants.REPORT_TIME_YEAR) {
			sql += " AND r.report_timetype = @reportTimetype AND r.report_year = @reportYear AND r.report_year_time = @reportYearTime ";
			params.put("reportYearTime", indicatorAccountVO.getYearTime());
		} else {
			sql += " AND r.report_timetype = @reportTimetype AND r.report_year = @reportYear AND r.quarter = @quarter ";
			params.put("quarter", reportTimeVO.quarter);
		}

		// 引用指标值不存在，按0进行处理
		Double indicatorValue = null;
		try {
			indicatorValue = sqlTemplate.queryForObject(sql, vars, params, Double.class);
			if (indicatorValue == null) {
				indicatorValue = Double.valueOf("0");
			}
		} catch (Exception e) {
			logger.error(
					String.format("获取核算指标引用的指标时发生错误，核算指标信息:%s,引用指标信息:%s", Json.toJson(indicatorAccounting),
							Json.toJson(reference)), e);
			indicatorValue = Double.valueOf("0");
		}

		Double result = reducingResultMagnitude(indicatorValue, reference.getReferenceIndicatorType(), referenceIndicatorId);
		
		
		/***************************************记录上报报表信息*****************************************/
		String reportTSql = "select * from TBL_REPORT_TYPE t where t.id = '"  + params.get("reportTypeId") + "'"; 
		Record record = sqlTemplate.queryForRecord(reportTSql, null);
		String clomunName = "name";
		String indicatorTSql = "select * from T_INDICATOR t where t.id = '"  + reference.getReferenceIndicatorId() + "'"; 		
		if (IndicatorAccountingReferenceType.INDICATOR_GROUP.getValue() == reference.getReferenceIndicatorType()) {
			indicatorTSql = "select * from T_INDICATOR_ACC_REF_GROUP t where t.id = '"  + reference.getReferenceIndicatorId() + "'"; 
			clomunName = "group_name";
		}		
		Record indicatorRecord = sqlTemplate.queryForRecord(indicatorTSql, null);
		setIndicatorProcess("上报报表", indicatorAccounting.getUnit(), indicatorRecord.getString(clomunName), 
				result.toString(),record.getString("type_no") + "-" +  record.getString("type_name"), record.getString("id"));
		/***************************************记录上报报表信息*****************************************/
		
		return result;
	}

	/**
	 * 指标上报频率与报表时间类型转换.
	 * 
	 * @param reference
	 * @param indicatorAccountVO
	 */
	private ReportTimeVO frequencyConverter(IndicatorAccounting indicatorAccounting, Integer referenceIndicatorId,
			IndicatorAccountVO indicatorAccountVO) {
		ReportTimeVO reportTime = null;
		// 年份
		if (indicatorAccountVO.getTimeType() == Constants.REPORT_TIME_YEAR) {
			Cnd cnd = Cnd.where("id", "=", referenceIndicatorId).and("indicator_type", "<", 4);
			Record record = indicatorSystemDao.fetch("V_INDICATOR_ACCOUNT_REF_SOU", cnd);
			if (record == null) {
				throw Lang.makeThrow("核算指标引用指标不存在,核算指标：%s，引用指标：%s", indicatorAccounting, cnd);
			}
			int frequency = record.getInt("indicator_type");

			// 半年报
			if (indicatorAccountVO.getYearTime() != null && indicatorAccountVO.getYearTime() == 1) {
				if (frequency == 3) {
					// 返回 类型 季报 季度为4
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_QUARTER, indicatorAccountVO.getYear(), null, 2,
							null);
				} else if (frequency == 4) {
					// 返回类型为 月报 月份为12
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_MONTH, indicatorAccountVO.getYear(), null,
							null, 6);

				} else {
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_YEAR, indicatorAccountVO.getYear(), 1, null,
							null);

				}

			} else {
				// 年报
				if (frequency == 3) {
					// 返回 类型 季报 季度为4
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_QUARTER, indicatorAccountVO.getYear(), null, 4,
							null);
				} else if (frequency == 4) {
					// 返回类型为 月报 月份为12
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_MONTH, indicatorAccountVO.getYear(), null,
							null, 12);
				} else {
					reportTime = new ReportTimeVO(Constants.REPORT_TIME_YEAR, indicatorAccountVO.getYear(), 2, null,
							null);
				}
			}
		}
		return reportTime;
	}

	/**
	 * 增加指标所对应的报表参数,包括报表id，所在行列
	 * 
	 * @param reference
	 * @param params
	 */
	private void addIndicatorParam(Integer referenceIndicatorId, IndicatorAccountVO indicatorAccountVO,
			Map<String, Object> vars, Map<String, Object> params) {
		ReportTypeIndicator reportTypeIndicatorRel = getReportTypeIndicatorRel(referenceIndicatorId, indicatorAccountVO);

		if (reportTypeIndicatorRel == null) {
			logger.error("无法获取指标与报表的关系，引用指标id：{},其他条件：{}", referenceIndicatorId, indicatorAccountVO);
			throw Lang.makeThrow("无法获取指标与报表的关系");
		} else {

			String reportTypeId = reportTypeIndicatorRel.getReportTypeId();
			Integer row = reportTypeIndicatorRel.getRow();
			Integer clomun = reportTypeIndicatorRel.getTheColumn();

			params.put("reportTypeId", reportTypeId);
			if (row != null) {
				params.put("row", row);
			}
			vars.put("clomun", "f" + clomun.intValue());
		}
	}

	/**
	 * 获取报表与指标的关系.
	 * 
	 * @param reference
	 * @param indicatorAccountVO
	 * @param reportStatSystem
	 * @return
	 */
	private ReportTypeIndicator getReportTypeIndicatorRel(Integer referenceIndicatorId,
			IndicatorAccountVO indicatorAccountVO) {
		String sql = "SELECT rtip.* FROM t_report_type_indicator rtip INNER JOIN tbl_report_type rt ON rtip.report_type_id = rt.id "
				+ " INNER JOIN tbl_report_version rv "
				+ " ON rt.version_id = rv.id AND rv.start_date <= @year AND rv.end_date >= @year "
				+ " WHERE rtip.indicator_id = @indicatorId";
		Map<String, Object> params = Maps.newHashMap();
		SimpleDateFormat df = new SimpleDateFormat("yyyy");
		try {
			params.put("year", df.parse(indicatorAccountVO.getYear().toString()));
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		params.put("indicatorId", referenceIndicatorId);
		Entity<ReportTypeIndicator> entity = indicatorSystemDao.getEntity(ReportTypeIndicator.class);
		return indicatorSystemSqlTemplate.queryForObject(sql, params, entity);

	}

	/**
	 * 将指标查询结果还原统一的数量级.
	 * 
	 * @param result 查询到的指标值
	 * @param reference 引用指标信息
	 * @return
	 */
	public Double reducingResultMagnitude(Double result, Integer refIndicatorType, Integer refIndicatorId) {
		Double indicatotUnitMagnitude = 1d;
		Cnd cnd = null;
		if (IndicatorAccountingReferenceType.INDICATOR_GROUP.getValue() == refIndicatorType) {
			cnd = Cnd.where("id", "=", refIndicatorId).and("indicator_type", "<=", 4);
		} else {
			cnd = Cnd.where("id", "=", refIndicatorId).and("indicator_type", "=", refIndicatorType);
		}

		Record record = indicatorSystemDao.fetch("V_INDICATOR_ACCOUNT_REF_SOU", cnd);
		if (record != null) {
			if (record.get("unit_magnitude") != null) {
				indicatotUnitMagnitude = Castors.me().castTo(record.get("unit_magnitude"), Double.class);
			}
		}
		return result * indicatotUnitMagnitude;
	}
	
	public synchronized List<Map> getIndicatorProcess(String accountingResultId){
		
		indicatorProcess = new ArrayList<Map>();
		
		Record accountingResult = dao.fetch("TBL_INDICATOR_ACCOUNT", Cnd.where("ID", "=", accountingResultId));

		int indicatorAccountId = accountingResult.getInt("INDICATOR_ID");
		IndicatorAccounting indicatorAccounting = indicatorSystemDao.fetch(IndicatorAccounting.class,
				indicatorAccountId);

		IndicatorAccountVO vo = getAccountVOFromResult(accountingResult);
		// 计算本指标
		Double result = calculateIndicator(vo, indicatorAccounting);
		
		return indicatorProcess;
	}
	
	public void setIndicatorProcess(String indicatorType, String indicatorUnit, String indicatorName, String indicatorValue, String indicatorSource, String id){
		
		if (indicatorProcess!=null){
			
			Map indicatorMap = new HashMap();
			indicatorMap.put("indicatorType", indicatorType);
			indicatorMap.put("indicatorUnit", indicatorUnit);
			indicatorMap.put("indicatorName", indicatorName);
			indicatorMap.put("indicatorValue", indicatorValue);
			indicatorMap.put("indicatorSource", indicatorSource);
			indicatorMap.put("id", id);
			
			indicatorProcess.add(indicatorMap);
		}

	}
	
	/**
	 * 查询指标单位
	 * 
	 * @param result 查询到的指标值
	 * @param reference 引用指标信息
	 * @return
	 */
	private String reducingResultUnit(Integer refIndicatorType, Integer refIndicatorId) {
		String indicatotUnit = "";
		Cnd cnd = null;
		if (IndicatorAccountingReferenceType.INDICATOR_GROUP.getValue() == refIndicatorType) {
			cnd = Cnd.where("id", "=", refIndicatorId).and("indicator_type", "<=", 4);
		} else {
			cnd = Cnd.where("id", "=", refIndicatorId).and("indicator_type", "=", refIndicatorType);
		}

		Record record = indicatorSystemDao.fetch("V_INDICATOR_ACCOUNT_REF_SOU", cnd);
		if (record != null) {
			if (record.get("unit") != null) {
				indicatotUnit = Castors.me().castTo(record.get("unit"), String.class);
			}
		}
		return indicatotUnit;
	}

	/**
	 * 报表时间值对象.
	 */
	class ReportTimeVO {
		Integer timeType;
		Integer year;
		Integer yearTime;
		Integer quarter;
		Integer month;

		public ReportTimeVO(Integer timeType, Integer year, Integer yearTime, Integer quarter, Integer month) {
			this.timeType = timeType;
			this.year = year;
			this.yearTime = yearTime;
			this.quarter = quarter;
			this.month = month;
		}

	}
}
